package com.java.simple.study.jvm.stringtable;

import com.java.simple.study.tool.Sleeper;

/**
 * @author zhoujunlin
 * @date 2022年09月08日 22:55
 * @desc 对于程序中大量存在存在的字符串，尤其其中存在很多重复字符串时，使用intern()可以节省内存空间。
 */
public class StringIntern {

    public static void main(String[] args) {
        test6();
    }

    static final int MAX_COUNT = 1000_0000;
    static final String[] arr = new String[MAX_COUNT];

    public static void test6() {
        Integer[] data = new Integer[]{1, 2, 3, 4, 5};
        long start = System.currentTimeMillis();
        for (int i = 0; i < MAX_COUNT; i++) {
            arr[i] = new String(String.valueOf(data[i % data.length])); // 大概有1000万String实例对象
            //arr[i] = new String(String.valueOf(data[i % data.length])).intern(); // 大概只有100万String实例对象
        }
        long end = System.currentTimeMillis();
        System.out.println("花费时间为：" + (end - start) + "ms");  // 无intern:7203ms  intern:1188ms
        Sleeper.sleep(1000);
        System.gc();
    }


    public static void test5() {
        String s1 = new String("a") + new String("b");
        System.out.println(System.identityHashCode(s1)); // 2016447921
        s1.intern();
        System.out.println(System.identityHashCode(s1)); // 2016447921
        String s2 = "ab";
        System.out.println(System.identityHashCode(s2)); //2016447921
        System.out.println(s1 == s2);
    }

    public static void test4() {
        String ab = new String("ab");
        ab.intern();
        String s2 = "ab";
        System.out.println(ab == s2);
    }

    public static void test3() {
        // 字符串常量池中产生"ab"
        String x = "ab";
        // s指向堆中的一个对象  字符串常量池中新增"a" "b"
        String s = new String("a") + new String("b");
        // 常量池中已经存在  不会再放入
        String s2 = s.intern();
        // true
        System.out.println(s2 == "ab");
        //jdk6 false jdk8 false
        System.out.println(s == "ab");
    }


    public static void test2() {
        // s指向堆中的一个对象  字符串常量池中新增"a" "b"
        String s = new String("a") + new String("b");
        // jdk6中：在字符串常量池（此时在永久代）中创建一个字符串"ab"
        // jdk8字符串常量池（此时在堆中）中没有创建字符串"ab",而是创建一个引用，指向new String("ab")，将此引用返回
        String s2 = s.intern();
        // true
        System.out.println(s2 == "ab");
        //jdk6 false jdk8 true
        System.out.println(s == "ab");
    }

    public static void test1() {
        // 此时 堆中有个对象  常量池1个对象
        String s = new String("1");
        // 这里常量池中已经有1了
        s.intern();
        String s2 = "1";
        System.out.println(s == s2);  // jdk6及以后都是false

        /**
         * 常量池中已经有1了所以这里有 4个对象
         * 1. StringBuilder
         * 2. new String - 1
         * 3. new String - 1
         * 4. new String - 11
         * 且11并没有放入常量池
         *
         */
        String s3 = new String("1") + new String("1");
        /**
         * 1. 在JDK6的版本中，字符串常量池还在永久代，所以直接在永久代生成"11",也就有了新的地址
         * 2. 而在JDK7的后续版本中，字符串常量池被移动到了堆中，此时堆里已经有new String（"11"）了
         * 出于节省空间的目的，直接将堆中的那个字符串的引用地址储存在字符串常量池中。没错，字符串常量池
         * 中存的是new String（"11"）在堆中的地址
         */
        s3.intern();
        String s4 = "11";
        System.out.println(s3 == s4); // jdk6是false   jdk6之后是true
    }

}
